﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AForge.Video.DirectShow;
using System.Drawing;
using LogLib;
using System.IO;
using NewCapturerDemo.Config;
using System.Windows.Forms;
using System.Threading.Tasks;

namespace NewCapturerDemo
{
    public class Camera
    {
        #region static fields and functions

        private static VideoCapabilities[] _videoCapabilities;

        private static FilterInfoCollection VideoDevices;

        static Camera()
        {
            InitFilterInfoCollection();
        }

        private static void InitFilterInfoCollection()
        {
            if (VideoDevices != null)
            {
                VideoDevices.Clear();
            }
            VideoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
        }

        public static List<KeyValuePair<string, string>> GetAllDevices()
        {
            InitFilterInfoCollection();
            List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
            foreach (FilterInfo info in VideoDevices)
            {
                list.Add(new KeyValuePair<string, string>(info.Name, info.MonikerString));
            }
            return list;
        }
        #endregion

        private string _camName = "";

        private CameraOption _option = new CameraOption();
        public CameraOption Option
        {
            get { return _option; }
        }

        private VideoCaptureDevice _capture;
        public VideoCaptureDevice Capture
        {
            get
            {
                return _capture;
            }
        }

        public CamProperties Properties;

        /// <summary>
        /// 错误信息
        /// </summary>
        private string _errorMessage = "";
        public string ErrorMessage
        {
            get { return _errorMessage; }
            set { _errorMessage = value; }
        }

        /// <summary>
        /// 镜头控制对象是否创建成功
        /// </summary>
        private bool _bCapturePicked = false;
        public bool CapturePicked
        {
            get { return _bCapturePicked; }
            set { _bCapturePicked = value; }
        }

        public Camera()
        {
            LoadConfig();
        }

        public void LoadConfig()
        {
            if (!File.Exists(_option.ConfigFilePath))
            {
                _option.Save();
            }
            if (_option.Load())
            {              
                Log.WriteAsync("镜头选项加载成功.");
            }
            else
            {
                MessageBox.Show(_option.ErrorMessage);
                Log.WriteAsync("镜头选项加载失败.");
            }
        }
     
        public bool Connected()
        {
            _errorMessage = "";
            if (VideoDevices.Count == 0)
            {
                _errorMessage = "计算机未发现任何图像设备, \n请检查镜头USB是否正常连接";
                return false;
            }
            return true;
        }


        public int IndexOf(string name)
        {
            int i = -1;
            foreach (FilterInfo info in VideoDevices)
            {
                if (name.Equals(info.Name))
                {
                    return i;
                }
                i++;
            }
            return -1;
        }

        public bool ExistCamera(string moniker)
        {
            _errorMessage = "";
            if (string.IsNullOrEmpty(moniker))
            {
                _errorMessage = "没有指定任何的图像设备作为拍照镜头";
                return false;
            }
            if (!Connected())
            {
                return false;
            }
            bool find = false;
            foreach (FilterInfo info in VideoDevices)
            {
                if (moniker.Equals(info.MonikerString))
                {
                    find = true;
                    break;
                }
            }
            if (!find)
            {
                _errorMessage = string.Format("没有发现指定的镜头设备: {0}.\n请打开配置管理程序，正确配置拍照首选项.", moniker);
            }
            return find;
        }

        /// <summary>
        /// 设置镜头分辨率
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public void SetFrameSize(Size size)
        {
            //设置摄像头参数
            int nIndex = -1;
            for (int i = 0; i < _videoCapabilities.Length; i++)
            {
                if (_videoCapabilities[i].FrameSize == size)
                {
                    nIndex = i;
                    break;
                }
            }

            if (nIndex >= 0)
            {
                _capture.VideoResolution = _videoCapabilities[nIndex];
            }
        }

        public void SelectIndex(int  index)
        {
            _option.Index = index;
            _option.CamName = VideoDevices[index].Name;
            _option.MonikerString = VideoDevices[index].MonikerString;
        }

        /// <summary>
        /// 获取并创建指定ID配置的相机控制对象
        /// </summary>
        /// <param name="opt"></param>
        /// <returns></returns>
        public bool PickTheCapture()
        {
            if (_bCapturePicked)
            {
                return true;
            }
            _errorMessage = "";
            if (!Connected())
            {
                return false;
            }
            int index = 0;
            if (ExistCamera(_option.MonikerString))
            {
                _capture = new VideoCaptureDevice(_option.MonikerString);
                _camName = _option.CamName;
            }
            else
            {
                index = IndexOf(_option.CamName);
                if (index >= 0)
                {
                    _capture = new VideoCaptureDevice(VideoDevices[index].MonikerString);
                    _camName = VideoDevices[index].Name;
                }
                else
                {
                    _capture = new VideoCaptureDevice(VideoDevices[0].MonikerString);
                    _camName = VideoDevices[0].Name;
                }
            }
            _videoCapabilities = _capture.VideoCapabilities;
            if (!SupportFrameSize(_option.FrameSize))
            {
                _errorMessage = string.Format("{0}不支持的分辨率: {1}×{2}", _camName, _option.FrameSize.Width, _option.FrameSize.Height);
                _capture = null;
                return false;
            }

            // 初始化镜头各项参数
            _capture.ProvideSnapshots = false;
            _capture.IsCrop = true;
            SetFrameSize(_option.FrameSize);
            _capture.CropRect = _option.ValidRect;

            Properties = new CamProperties(_capture);          
            Properties.GetCurrCamPropValueAsync();
            if (ReadCamNameFromIni() != _camName)
            {
                Properties.GetDefaultCamPropsAsync();
                Properties.SaveDefaultCamProps();
                SaveCamNameToIni(_camName);
                _option.CamName = _camName;
                _option.Save();
            }

            // 当前相机参数
            string[] strArray = Properties.StringValues();
            Log.WriteAsync(strArray.ToList());
            _bCapturePicked = true;
            return true;
        }

        public bool SupportFrameSize(Size s)
        {
            for (int i = 0; i < _videoCapabilities.Length; i++)
            {
                if (_videoCapabilities[i].FrameSize == s)
                {
                    return true;
                }
            }
            return false;
        }

        private string ReadCamNameFromIni()
        {
            return IniCom.IniValue("CamName", "name");
        }

        private void SaveCamNameToIni(string name)
        {
            IniCom.WriteValue("CamName", "name", name);
        }

        public bool InitProperties()
        {
            _errorMessage = "";
            try
            {
                Properties.LoadPopertiesFromIni();
                SetBrightness(Properties[CamProc.Brightness].Value);
                SetContrast(Properties[CamProc.Contrast].Value);
                SetHue(Properties[CamProc.Hue].Value);
                SetSaturation(Properties[CamProc.Saturation].Value);
                SetSharpness(Properties[CamProc.Sharpness].Value);
                SetFocus(Properties[CamProc.Focus].Value, Properties[CamProc.Focus].Auto);
                SetExposure(Properties[CamProc.Exposure].Value, Properties[CamProc.Exposure].Auto);
                SetWhiteBalance(Properties[CamProc.WhiteBalance].Value, Properties[CamProc.WhiteBalance].Auto);
            }
            catch (ApplicationException ex)
            {
                _errorMessage = ex.Message;
                Log.WriteException(ex.ToString());
                return false;
            }
            return true;
        }

        public double GetBrightness()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.Brightness, out nValue, out flag);
            return (double)nValue;
        }

        public double GetExposure()
        {
            int nValue = 0;
            CameraControlFlags flag = new CameraControlFlags();
            _capture.GetCameraProperty(CameraControlProperty.Exposure, out nValue, out flag);
            return (double)nValue;
            //return _capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.Exposure);
        }

        public double GetFocus()
        {
            int nValue = 0;
            CameraControlFlags flag = new CameraControlFlags();
            _capture.GetCameraProperty(CameraControlProperty.Focus, out nValue, out flag);
            return (double)nValue;
        }

        public void SetBrightness(double value)
        {
            _capture.SetVideoProperty(VideoProcAmpProperty.Brightness, (int)value, VideoProcAmpFlags.Manual);
        }

        public void SetExposure(double value, bool auto = false)
        {
            _capture.SetCameraProperty(
                    CameraControlProperty.Exposure,
                    (int)value,
                    auto ? CameraControlFlags.Auto : CameraControlFlags.Manual);
        }

        public void SetFocus(double value, bool auto = false)
        {
            _capture.SetCameraProperty(
                CameraControlProperty.Focus,
                (int)value,
                auto ? CameraControlFlags.Auto : CameraControlFlags.Manual);
        }

        public void SetContrast(double value)
        {
            _capture.SetVideoProperty(VideoProcAmpProperty.Contrast, (int)value, VideoProcAmpFlags.Manual);
        }

        public double GetContrast()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.Contrast, out nValue, out flag);
            return (double)nValue;
        }

        public void SetHue(double value)
        {
            _capture.SetVideoProperty(VideoProcAmpProperty.Hue, (int)value, VideoProcAmpFlags.Manual);
        }

        public double GetHue()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.Hue, out nValue, out flag);
            return (double)nValue;
        }

        //Staturation
        public void SetSaturation(double value)
        {
            _capture.SetVideoProperty(VideoProcAmpProperty.Saturation, (int)value, VideoProcAmpFlags.Manual);
        }

        public double GetSaturation()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.Saturation, out nValue, out flag);
            return (double)nValue;
        }

        public void SetSharpness(double value)
        {
            _capture.SetVideoProperty(VideoProcAmpProperty.Sharpness, (int)value, VideoProcAmpFlags.Manual);
        }

        public double GetSharpness()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.Sharpness, out nValue, out flag);
            return (double)nValue;
        }

        public void SetGamma(double value)
        {
            _capture.SetVideoProperty(VideoProcAmpProperty.Gamma, (int)value, VideoProcAmpFlags.Manual);
        }

        public double GetGamma()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.Gamma, out nValue, out flag);
            return (double)nValue;
        }

        public void SetGain(double value)
        {
            _capture.SetVideoProperty(VideoProcAmpProperty.Gain, (int)value, VideoProcAmpFlags.Manual);
        }

        public double GetGain()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.Gain, out nValue, out flag);
            return (double)nValue;
        }

        public void SetWhiteBalance(double value, bool auto = false)
        {
            _capture.SetVideoProperty(
                VideoProcAmpProperty.WhiteBalance,
                (int)value,
                auto ? VideoProcAmpFlags.Auto : VideoProcAmpFlags.Manual);
        }

        public double GetWhiteBalance()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.WhiteBalance, out nValue, out flag);
            return (double)nValue;
        }

        public void SetBacklightCompensation(double value)
        {
            _capture.SetVideoProperty(VideoProcAmpProperty.BacklightCompensation, (int)value, VideoProcAmpFlags.Manual);
        }

        public double GetBacklightCompensation()
        {
            int nValue = 0;
            VideoProcAmpFlags flag = new VideoProcAmpFlags();
            _capture.GetVideoProperty(VideoProcAmpProperty.BacklightCompensation, out nValue, out flag);
            return (double)nValue;
        }
    }
}
